source("utils.R")Discrete Marks
Dependencies
Setup
spe <- readRDS("../data/spe.rds")Load data
sub <- spe[, spe$sample_id == 0.01]
(pp <- .ppp(sub, marks = "cluster_id"))Marked planar point pattern: 6111 points
marks are of storage type 'character'
window: rectangle = [1222.5635, 3012.4248] x [-3993.535, -2202.755] units
For simplicity we will mostly look at the three cell types Ependymal, OD Mature and Microglia in this vignette.
marks(pp) <- factor(marks(pp))
selection <- c('OD Mature', 'Ependymal', 'Microglia')
pp_sel <- subset(pp, marks %in% selection, drop = TRUE)Visualize
plot(pp)The summary of ppp (point pattern) object returns general properties, plus intensities, combined and per mark type.
summary(pp)Marked planar point pattern: 6111 points
Average intensity 0.001906561 points per square unit
Coordinates are given to 4 decimal places
Multitype:
frequency proportion intensity
Ambiguous 773 0.126493200 2.411670e-04
Astrocyte 646 0.105711000 2.015445e-04
Endothelial 469 0.076746850 1.463225e-04
Ependymal 268 0.043855340 8.361288e-05
Excitatory 1180 0.193094400 3.681463e-04
Inhibitory 1965 0.321551300 6.130571e-04
Microglia 97 0.015873020 3.026287e-05
OD Immature 200 0.032727870 6.239767e-05
OD Mature 457 0.074783180 1.425787e-04
Pericytes 56 0.009163803 1.747135e-05
Window: rectangle = [1222.5635, 3012.4248] x [-3993.535, -2202.755] units
(1790 x 1791 units)
Window area = 3205250 square units
The stationarity assumption is not appropriate in all cases. To assess stationarity visually we can plot the kernel density estimates, for all points together or per type.
plot(density(pp))ppls <- split(pp_sel) # split by mark
plot(density(ppls))This indicates that our dataset seems to be inhomogeneous, i.e. the different cell types have different intensity functions
If we plot all on the same estimated intensities on the same scale, the very low intensity of Microglia gets obvious.
plot(density(ppls), zlim = c(0, max(density(ppls))))The relative risk, i.e. the probability of observing a given celltype at a given location, can be calculated with relrisk. The bandwidth for smoothing is calculated with bw.relrisk and might need to be adjusted.
rp <- relrisk(pp_sel,se=TRUE)
plot(rp$estimate)Using the relrisk function we can get the dominant mark for different regions of the tissue of interest.
Random labeling
A test of segregation of marks can be performed with segregation.test. It is based on the assumption of random labeling and the computed test statistic measures the similarity of the distribution of marks. The randomization is done by permuting the marks of the points while keeping the locations constant.
ppMarked planar point pattern: 6111 points
Multitype, with levels =
Ambiguous Astrocyte Endothelial Ependymal Excitatory Inhibitory Microglia OD
Immature OD Mature Pericytes
window: rectangle = [1222.5635, 3012.4248] x [-3993.535, -2202.755] units
segregation.test(pp,nsim=19)Computing observed value... Done.
Computing 19 simulated values... 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19.
Done.
Monte Carlo test of spatial segregation of types
data: pp
T = 504.23, p-value = 0.05
In this case there is clear indication that the labeling of marks is not random. For more about random labeling check the part in umarked about local scaling.
#TODO: shall we move this part to umarked as well, it goes together with the hotspot analysis
Measures of spatial segregation
The package seg implements different measures of spatial segregation. Spatial segregation measures encompass two distinct concepts. The first describes the evenness versus clustering which is independent of the overall composition of a sample and describes the distribution of individuals. The second describes isolation versus exposure which depends on the overall composition of a sample and describes the degree to which different groups are isolated or exposed to one another.
Spatial segregation measures developped by Reardon and O’Sullivan (2004) [Reardon, S. F. and O’Sullivan, D. (2004) Measures of spatial segregation. Sociological Methodology, 34, 121-162.]. D, R and H measure spatial evenness while P measures the spatial exposure.
- Dissimilarity index (D) describes how different the composition of a local environment is, on average, from the composition of the total population. It ranges from 0 to 1, where 0 indicates perfect integration, and 1 indicates complete segregation.
- Relative diversity (R) is a measure of how much less diverse each individuals local environments are, on average, compared to the total population. It ranges from 0 to 1, where 0 indicates perfect integration, and 1 indicates complete segregation.
- Information theory index (H) is a measure of how much less diverse individuals local environments are, on average, than is the total population. It can be interpreted as a measure of the variation in the diversity of each individuals local environment. It ranges from 0 to 1, where 0 indicates no segregation, and 1 indicates maximum possible segregation, i.e. all subjects in each local environment belong to one group only.
- The spatial exposure index (P) expresses the degree of spatial exposure of group m to group n. It is the average percentage of group n in the local environments of each member of group m and ranges from 0 (no exposure) to 1 (maximal exposure).
# create an indicator matrix
model.matrix <- matrix(0, nrow=length(marks(pp_sel)), ncol=length(unique(marks(pp_sel))))
model.matrix[cbind(seq_along(marks(pp_sel)), marks(pp_sel))] <- 1
colnames(model.matrix) <- unique(marks(pp_sel))
spseg(pp_sel,
data = model.matrix,
smoothing = 'kernel')
Reardon and O'Sullivan's spatial segregation measures
Dissimilarity (D) : 0.7888
Relative diversity (R): 0.6726
Information theory (H): 0.671
Exposure/Isolation (P):
OD Mature Microglia Ependymal
OD Mature 0.88990090 0.02714462 0.08295448
Microglia 0.07471038 0.43711714 0.48817248
Ependymal 0.04826590 0.10319928 0.84853482
--
The exposure/isolation matrix should be read horizontally.
Read 'help(spseg)' for more details.
How to interpret the spatial exposure index P? Let’s look at the documentation of the function.
From help(spseg)
The exposure/isolation index, P, is presented in a matrix form. The spatial exposure of group ‘m’ to group ‘n’ is located in the row ‘m’ and column ‘n’ of the matrix. The matrix is rarely symmetric in practice so the spatial exposure index should be interpreted with care. The spatial isolation index values are given in the diagonal cells of the matrix; cell value at (m, m) indicates the degree of spatial isolation for group ‘m’ for example.
Spatial Proximity
Index of Spatial Proximity developed by White (1983) [White, M. J. (1983). The measurement of spatial segregation. The American Journal of Sociology, 88, 1008-1018.].
- This numerical value indicates the degree of segregation. A value of 1 indicates evenness in the sample, and values greater than 1.0 indicate clustering. If the index value is smaller than one, it indicates an unusual form of segregation (i.e., some groups are closer to other groups). It compares the average distance between members of one group with that between all individuals, irrespective of group assignment. It may change depend on the definition of distance.
# Index of spatial proximity
paste0('Index of spatial proximity: ',
isp(pp_sel,
data = model.matrix))[1] "Index of spatial proximity: 2.94890271873706"
Correlation and spacing
Distances and nearest neighbors
Investigating the nearest neighbor distance between point for all combinations of marks can be done as follows:
d <- nndist(pp_sel,by = marks(pp_sel))
a <- aggregate(d,by = list(from=marks(pp_sel)),min)
a from Ependymal Microglia OD Mature
1 Ependymal 3.225180 8.176169 12.959456
2 Microglia 8.176169 16.443565 6.215464
3 OD Mature 12.959456 6.215464 6.428006
Nearest neighbor correlations
A overall correlation between marks can be calculated with nncorr. It returns two values: unnormalised, which is the probability that a point and its nearest neighbor have the same type and normalised, which divides the unnormalised probability by the probability of random labeling. So a value close to 1 indicates random labeling. A value much larger than 1 means neighboring point are often of the same type.
nncorr(pp_sel)unnormalised normalised
0.8081321 1.8823711
Another possibility is to work with nearest neighborhood contingency tables to do statistical tests using the dixon function from the R package dixon. It allows to calculate the statistic “segregation of species” S which indicates either random labeling (if S=0), attraction (if S<0) or seggregation (if S>0).
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
99.
From To Obs.Count Exp. Count S Z p-val.Z
1 Ependymal Ependymal 262 87.16 1.96 20.04 0.0000
2 Ependymal Microglia 3 31.66 -1.07 -5.66 0.0000
3 Ependymal OD Mature 3 149.18 -2.04 -16.87 0.0000
4 Microglia Ependymal 9 31.66 -0.68 -4.92 0.0000
5 Microglia Microglia 21 11.34 0.32 2.50 0.0124
6 Microglia OD Mature 67 53.99 0.25 2.60 0.0094
7 OD Mature Ependymal 8 149.18 -1.43 -14.26 0.0000
8 OD Mature Microglia 69 53.99 0.12 2.35 0.0190
9 OD Mature OD Mature 380 253.83 0.60 11.43 0.0000
p-val.Nobs
1 0.01
2 0.01
3 0.01
4 0.01
5 0.05
6 0.01
7 0.01
8 0.02
9 0.01
Summary functions for pairs of types
Similar to the simple case without marks it is possible do estimate summary functions. More specifically the summary functions between different marks can be calculated. These summary functions assume that the multitype process is stationary which might not be an appropriate assumption in spatial omcis data, we therefore use inhomogeneous versions of the summary functions.
Cross K-function
The cross K-function is a summary function that measures the average number of points of type j within a distance r of a point of type i. The formula is given by
\[ K(r) = \frac{1}{\lambda_j} \mathbb{E} [t(u,r,X^{j})|u \in X^{i}] \]
where \(X^{i}\) is the point pattern of type \(i\) and \(t(u,r,X^{j})\) is the number of points of type \(j\) in a circle of radius \(r\) around \(u\).
First we plot an overview over the cross K function for the different types.
plotCrossAll <- function(ppp, fun, edgecorr){
Fall <- alltypes(ppp, fun)
# Create a list of ggplot objects using lapply
plot_list <- lapply(Fall[["fns"]], function(res) {
ggplot(res, aes(x = r, y = .data[[edgecorr]])) +
geom_line(size = 1) +
geom_line(aes(x = r, y = theo), linetype = "dotted", size = 1) +
geom_line() +
labs(title = attributes(res)$yexp) +
theme_minimal()
})
p <- wrap_plots(plot_list, nrow = ceiling(length(plot_list)/3), ncol = 3) +
plot_layout(guides = "collect") & theme(legend.position='bottom')
return(p)
}plotCrossAll(pp_sel, "Kcross.inhom", "iso")The diagonal of the cross K-function plot shows the K-function for the different marks (indication of Poisson or non-Poisson point processes). Off-diagonal panels give indication of independence of points when the number of points follows the expected K-function but does not imply that the individual marks follow a Poisson process. If the types are independent they are also uncorrelated.
In this overview we can see that there is indication that Microglia and OD Mature cells are independent of each other. The other types seem to be dependent on each other. Let’s focus a bit more on the relationship between Ependymal and the other two cell types. We will also calculate confidence intervals for the different cross K-functions.
However, it is important to remember that the cross K-function assumes that the multitype process is stationary. If this is not the case, there is a risk in misinterpreting the results. The problem is confounding between clustering and inhomogeneity. We have already seen that our dataset most likely does not follow the assumption of stationarity. For this reason we will calculate the inhomogneous cross K-function.
plotCrossMetric <- function(ppp, fun, from, to, edgecorr){
lce <- lohboot(ppp, fun, from = from, to = to)
p <- ggplot(lce, aes(x = r, y = .data[[edgecorr]])) +
geom_line(size = 1) +
geom_ribbon(aes(ymin = lo, ymax = hi), alpha = 0.25)+
geom_line(aes(x = r, y = theo), linetype = "dotted", size = 1) +
geom_line() +
labs(title = attributes(lce)$yexp) +
theme_minimal()
return(p)
}
p_epen_od <- plotCrossMetric(pp_sel, "Kcross.inhom", "Ependymal", "OD Mature", "iso")
p_epend_micro <- plotCrossMetric(pp_sel, "Kcross.inhom", "Ependymal", "Microglia", "iso")# fig-width: 10
# fig-height: 10
p_epen_od + p_epend_microRemember that the dashed line represents the assumption of a multitype Poisson process. If the line lies above the dotted line there is indication of clustering while if the line is below the dotted line there is indication of repulsion. In the plot above we can see that there is indication of clustering between Ependymal and OD Mature cells while there is indication of repulsion between Ependymal and Microglia cells.
Cross L-function
Alternatively the L cross function with similar interpreation can be calculated using the Lcross function.
# fig-width: 10
# fig-height: 10
p_epen_od + p_epend_microMark connection function
The mark connection function is essentially the cross pair-correlation function, i.e. the generalization of the of the pair correlation function to multitpye point processes, divided by the unmarked pair-correlation function. It can be interpreted as the conditional probability that two points a distance r apart have labels of type 1 and of type 2, given the presence of those points.
plotCrossAll(pp_sel, "markconnect", "iso") + scale_y_continuous(limits = c(0, 1))The dashed lines indicate expected values under random labeling. The values measures dependence or association between the different points. Positive values indicate that nearby points are more likely to have different types than expected by chance. This positive association between different cell types does not necessarily imply dependence, as it could be influenced by a negative association between cells of the same type, as it is the case for the Microglia cells.
Cross F-function (empty space function), cross G-function (Nearest-neighbor function) and cross J-function
The cross F-function is the cumulative distribution function of the distance from a location to the nearest point of the same type. For each type \(i\) it is defined as
\[F_i(r) = \mathbb{P}\{d(u,X^{i}\leq r\}\].
The cross G-function is the cumulative distribution function of the distance from a location to the nearest point of another type and is defined as
\[G_{ij}(r) = \mathbb{P}\{d(x,X^{(j)} \setminus u \leq r \mid X^{(i)} \ \text{has a point at u})\].
If the points are independent of each other the G and F function are identical. Both assume that the process is stationary.
There exists a difference in the interpretation of the theoretical values of the K-cross and the G-cross function. For the K-cross the theoretical value indicates independence between the marks while for the G-cross the theoretical value is consistent with the assumption that the points of type j are Poisson in addition to being independent of the points of type \(i\).
The cross J-function is defined as
\[J_{ij}(r) = \frac{1-G_{ij}(r)}{1-F_{j}(r)}\]
and summarizes the interpoint dependence between type \(i\) and \(j\). Under the hypothesis of independent components, i.e. that the point processes of each type are independent the G-function is equivalent to the F-function and the J-function is equal to 1.
Dot functions
For each K-, G- and J- function there also exist dot functions which is measuring distances from points of one type to points of any type. These functions allow us to measure the dependence of one mark with all other marks. For expample, the K-dot function represents the expected number of other point within distance \(r\) of a typical point of type \(i\).
plotCrossAll(pp_sel, "Kdot.inhom", "iso")The dot functions are useful summary statistic to analyse the dependence of one mark with all other marks.
Summary function within and between types
In our original dataset we have a large number of different marks. We picked the three OD mature, Ependymal and Microglia for illustrative purposes. An alternative to looking at all cross summary function combinations, it is possible to compare between and within types. An alternative is to compare within and between types.
Mark equality function
The Mark or Type Equality function for a stationary multitype point process measures the correlation between types of two points separated by distance r. It is the sum of the mark connection function of all pairs of points of the same type.
If k < 1, points at distance r are less likely than expected to be of the same type. If > 1, they are more likely to be of the same type. The value 1 indicates a lack of correlation.
plotMarkCorr <- function(pp, edgecorr = "iso") {
me <- markcorr(pp)
ggplot(me, aes(x = r, y = .data[[edgecorr]])) +
geom_line(size = 1) +
geom_line(aes(x = r, y = theo), linetype = "dotted", size = 1) +
geom_line() +
labs(title = attributes(me)$yexp) +
theme_minimal()
}
plotMarkCorr(pp)We can see that in our dataset, it is closer, the more likely it is to find points of the same type.
Tests of randomness and independence
In a multitype point process there are two interesting hypothesis: - random labeling hypothesis: the allocation of lables to the points is random - independent component hyopthesis: there is independence between different type of points If both statments are correct, the point pattern is considered to be complete spatially random and independent (CSRI), the marked analog to complete spatial randomness (CSR).
Testing random labelling
Random labeling test is most logical when the marks represents its status, which is not most appropriate assumption when considering cell types. Test for random labeling can be done using permutation test, in which the labels are randomly permuted. Random labeling can be assumed if the permuted datasets are statistically equivalent to the original dataset.
Testing the indepenence of components assumption
The i to j functions are useful to test the independence of different subprocesses. If the processes of type i and j are independent then \(K_{ij} = \pi r^2, G_{ij}(r) = F_{j}(r), J_{ij}(r) \equiv 1\). Alternatively, randomization tests can be used in which simulated patterns from the dataset are generated and randomly split into subpatterns. These are then compared to the null hypothesis in which all subpatterns should be statistically equivalent to the original. However, this approach assumes stationarity and there is a need to handle edge effects appearing from random shifts when splitting.
plotEnvCross <- function(pp, i, j, fun, nsim = 39, radius = 150, global = FALSE){
pp_scaled <- rescale(pp)
E1 <- envelope(pp_scaled, fun, nsim=nsim, i=i, j=j,
simulate=expression(rshift(pp_scaled, radius = radius)), global = global)
p <- ggplot(E1, aes(x = r, y = .data[["mmean"]])) +
geom_line(size = 1) +
geom_ribbon(aes(ymin = lo, ymax = hi), alpha = 0.25)+
geom_line(aes(x = r, y = obs), linetype = "dotted", size = 1) +
geom_line() +
labs(title = attributes(E1)$yexp) +
theme_minimal()
return(p)
}
pEnv <- plotEnvCross(pp_sel, fun = "Kcross", "Ependymal", "OD Mature", nsim = 39, radius = 150)pEnvplotEnvCross(pp_sel, fun = "Kcross", "Ependymal", "OD Mature", nsim = 39, radius = 150, global = TRUE)Generating 78 simulations by evaluating expression (39 to estimate the mean and
39 to calculate envelopes) ...
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
78.
Done.
We have indication that the indepence of components assumption should not be rejected. Therefore we assume that Ependtymal and OD Mature cells are independent.
Continuous Marks
–> do we know any methods for this?
Appendix
Session info
sessionInfo()R version 4.3.1 (2023-06-16)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Monterey 12.6.1
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.11.0
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: Europe/Zurich
tzcode source: internal
attached base packages:
[1] stats4 stats graphics grDevices utils datasets methods
[8] base
other attached packages:
[1] stringr_1.5.0 dixon_0.0-8
[3] splancs_2.01-44 spdep_1.2-8
[5] spData_2.3.0 tmap_3.3-4
[7] scater_1.28.0 scran_1.28.2
[9] scuttle_1.10.3 SFEData_1.2.0
[11] SpatialFeatureExperiment_1.2.3 Voyager_1.2.7
[13] rgeoda_0.0.10-4 digest_0.6.33
[15] ncf_1.3-2 sf_1.0-14
[17] reshape2_1.4.4 patchwork_1.1.3
[19] STexampleData_1.8.0 ExperimentHub_2.8.1
[21] AnnotationHub_3.8.0 BiocFileCache_2.8.0
[23] dbplyr_2.3.4 RANN_2.6.1
[25] seg_0.5-7 sp_2.1-1
[27] rlang_1.1.1 ggplot2_3.4.4
[29] dplyr_1.1.3 mixR_0.2.0
[31] spatstat_3.0-6 spatstat.linnet_3.1-1
[33] spatstat.model_3.2-6 rpart_4.1.19
[35] spatstat.explore_3.2-3 nlme_3.1-162
[37] spatstat.random_3.1-6 spatstat.geom_3.2-5
[39] spatstat.data_3.0-1 SpatialExperiment_1.10.0
[41] SingleCellExperiment_1.22.0 SummarizedExperiment_1.30.2
[43] Biobase_2.60.0 GenomicRanges_1.52.1
[45] GenomeInfoDb_1.36.4 IRanges_2.34.1
[47] S4Vectors_0.38.2 BiocGenerics_0.46.0
[49] MatrixGenerics_1.12.3 matrixStats_1.0.0
loaded via a namespace (and not attached):
[1] spatstat.sparse_3.0-2 bitops_1.0-7
[3] httr_1.4.7 RColorBrewer_1.1-3
[5] tools_4.3.1 utf8_1.2.3
[7] R6_2.5.1 HDF5Array_1.28.1
[9] mgcv_1.8-42 rhdf5filters_1.12.1
[11] withr_2.5.1 gridExtra_2.3
[13] leaflet_2.2.0 leafem_0.2.3
[15] cli_3.6.1 labeling_0.4.3
[17] proxy_0.4-27 R.utils_2.12.2
[19] dichromat_2.0-0.1 scico_1.5.0
[21] limma_3.56.2 rstudioapi_0.15.0
[23] RSQLite_2.3.1 generics_0.1.3
[25] crosstalk_1.2.0 Matrix_1.5-4.1
[27] ggbeeswarm_0.7.2 fansi_1.0.5
[29] abind_1.4-5 R.methodsS3_1.8.2
[31] terra_1.7-55 lifecycle_1.0.3
[33] yaml_2.3.7 edgeR_3.42.4
[35] rhdf5_2.44.0 tmaptools_3.1-1
[37] grid_4.3.1 blob_1.2.4
[39] promises_1.2.1 dqrng_0.3.1
[41] crayon_1.5.2 lattice_0.21-8
[43] beachmat_2.16.0 KEGGREST_1.40.1
[45] magick_2.8.0 pillar_1.9.0
[47] knitr_1.44 metapod_1.7.0
[49] rjson_0.2.21 boot_1.3-28.1
[51] codetools_0.2-19 wk_0.8.0
[53] glue_1.6.2 vctrs_0.6.4
[55] png_0.1-8 gtable_0.3.4
[57] cachem_1.0.8 xfun_0.40
[59] S4Arrays_1.0.6 mime_0.12
[61] DropletUtils_1.20.0 units_0.8-4
[63] statmod_1.5.0 bluster_1.10.0
[65] interactiveDisplayBase_1.38.0 ellipsis_0.3.2
[67] bit64_4.0.5 filelock_1.0.2
[69] irlba_2.3.5.1 vipor_0.4.5
[71] KernSmooth_2.23-21 colorspace_2.1-0
[73] DBI_1.1.3 raster_3.6-26
[75] tidyselect_1.2.0 bit_4.0.5
[77] compiler_4.3.1 curl_5.1.0
[79] BiocNeighbors_1.18.0 DelayedArray_0.26.7
[81] scales_1.2.1 classInt_0.4-10
[83] rappdirs_0.3.3 goftest_1.2-3
[85] fftwtools_0.9-11 spatstat.utils_3.0-3
[87] rmarkdown_2.25 XVector_0.40.0
[89] htmltools_0.5.6.1 pkgconfig_2.0.3
[91] base64enc_0.1-3 sparseMatrixStats_1.12.2
[93] fastmap_1.1.1 htmlwidgets_1.6.2
[95] shiny_1.7.5.1 DelayedMatrixStats_1.22.6
[97] farver_2.1.1 jsonlite_1.8.7
[99] BiocParallel_1.34.2 R.oo_1.25.0
[101] BiocSingular_1.16.0 RCurl_1.98-1.12
[103] magrittr_2.0.3 GenomeInfoDbData_1.2.10
[105] s2_1.1.4 Rhdf5lib_1.22.1
[107] munsell_0.5.0 Rcpp_1.0.11
[109] ggnewscale_0.4.9 viridis_0.6.4
[111] stringi_1.7.12 leafsync_0.1.0
[113] zlibbioc_1.46.0 plyr_1.8.9
[115] parallel_4.3.1 ggrepel_0.9.4
[117] deldir_1.0-9 Biostrings_2.68.1
[119] stars_0.6-4 splines_4.3.1
[121] tensor_1.5 locfit_1.5-9.8
[123] igraph_1.5.1 ScaledMatrix_1.8.1
[125] BiocVersion_3.17.1 XML_3.99-0.14
[127] evaluate_0.22 BiocManager_1.30.22
[129] httpuv_1.6.11 purrr_1.0.2
[131] polyclip_1.10-6 rsvd_1.0.5
[133] lwgeom_0.2-13 xtable_1.8-4
[135] e1071_1.7-13 RSpectra_0.16-1
[137] later_1.3.1 viridisLite_0.4.2
[139] class_7.3-22 tibble_3.2.1
[141] memoise_2.0.1 beeswarm_0.4.0
[143] AnnotationDbi_1.62.2 cluster_2.1.4